#include <iostream>
#include <string>
#include <poll.h>
#include "Sock.hpp"

#define NUM 128 //数组长度
#define INVALID_FD -1 //无效fd

struct pollfd pollfds[NUM];

static void Usage(std::string proc)
{
    std::cout << "Usage: " << proc << " port" << std::endl;
}
//我们是后序这样启动服务器的: ./poll_server 端口号
int main(int argc,char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }
    //创建套接字,绑定,监听
    uint16_t port = atoi(argv[1]);
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock,port);
    Sock::Listen(listen_sock);

    //先将数组的所有位置的文件描述符设为无效
    for (int i = 0; i < NUM; i++)
    {
        pollfds[i].fd = INVALID_FD;
        pollfds[i].events = 0;
        pollfds[i].revents = 0;
	}

    //将监听套接字添加到数组0下标位置中,并关心其读事件
    pollfds[0].fd = listen_sock;
    pollfds[0].events = POLLIN;//关心listen_sock的读取事件是否就绪
    pollfds[0].revents = 0;

    for(;;)
    {
        int n = poll(pollfds, NUM, -1);//-1表示阻塞等待
        switch (n) 
        {
            case 0: //超时
                std::cout << "poll timeout" << std::endl;
                break;
            case -1: //调用失败
                std::cerr << "poll failed" << std::endl;
                break;
            default://有事件就绪了
                std::cout << "有fd对应的事件就绪啦!" << std::endl;
                //需要遍历数组,看是哪个文件描述符上的事件就绪了
                for(int i = 0;i<NUM;i++)
                {
                    if(pollfds[i].fd == INVALID_FD) //跳过无效的位置
                        continue;

                    if(pollfds[i].fd == listen_sock && pollfds[i].revents&POLLIN)//监听套接字上的读取事件就绪了
                    {
                        std::cout << "listen_sock: " << listen_sock << " 有了新的链接到来" << std::endl;

                        int sock = Sock::Accept(listen_sock);
                        if(sock>=0)
                        {
                            std::cout << "listen_sock: " << listen_sock << " 获取新的链接成功" << std::endl;    
                            //此时不能直接进行读取,因为新链接到来，不意味着有数据到来
                            //所以需要将新获取到的套接字添加到数组中,并关心其读事件是否就绪

                            //在数组中找一个没有被使用的位置
                            int pos = 1;
                            for(;pos<NUM;pos++)
                            {
                                if(pollfds[pos].fd  == INVALID_FD)
                                {
                                    break;
                                }
                            }
                            //跳出循环有两种情况:
                            if(pos < NUM)//case1: 找到了一个位置没有被使用
                            {
                                std::cout << "新链接: " << sock << " 已经被添加到了数组[" << pos << "]的位置" << std::endl;
                                pollfds[pos].fd = sock;
                                pollfds[pos].events = POLLIN;
                            }
                            else//case2: 没有空位置,说明服务器已经满载，没法处理新的请求了
                            {
                                std::cout << "服务器已经满载了，关闭新的链接" << std::endl;
                                close(sock);
                            }
                        }
                    }
                    else if(pollfds[i].revents &POLLIN) //普通文件描述符上的读事件就绪
                    {
                        std::cout << "sock: " << pollfds[i].fd << " 上面有了读事件，可以读取了" << std::endl;
                        char buffer[1024];
                        ssize_t s = read(pollfds[i].fd,buffer,sizeof(buffer)-1);
                        if(s>0)
                        {
                            buffer[s] = '\0';
                            std::cout << "client[ " << pollfds[i].fd << "]# " << buffer << std::endl;
                        }
                        else if(s == 0) //对端关闭了链接
                        {
                            std::cout << "sock: " << pollfds[i].fd << "关闭了, client退出啦!" << std::endl;
                            close(pollfds[i].fd);
                            std::cout << "已经在数组下标pollfds[" << i << "]"<< "中,去掉了sock: " << pollfds[i].fd<< std::endl;
                            //连接对应的文件描述符从fds数组当中清除,下一次调用poll函数时无需再监视该套接字的事件
                            pollfds[i].fd = INVALID_FD;
                            pollfds[i].events = 0;
		                    pollfds[i].revents = 0;
                        }
                        else//读取失败
                        {
                            close(pollfds[i].fd);
                            std::cout << "已经在数组下标pollfds[" << i << "]" << "中,去掉了sock: " << pollfds[i].fd << std::endl;
                            //连接对应的文件描述符从fds数组当中清除,下一次调用poll函数时无需再监视该套接字的事件
                            pollfds[i].fd = INVALID_FD;
                            pollfds[i].events = 0;
		                    pollfds[i].revents = 0;
                        }
                    }
                }
                break;
        }
    }
    return 0;
}